# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import netsvc
import time
from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT
import openerp.addons.decimal_precision as dp
import datetime
from openerp import workflow
#----------------------------------------------------------
# Sale Order Lines
#----------------------------------------------------------

class sale_make_invoice(osv.osv_memory):
    _inherit = "sale.advance.payment.inv"

    def create_invoices(self, cr, uid, ids, context=None):
        
        """ create invoices for the active sales orders """
        sale_obj = self.pool.get('sale.order')
        act_window = self.pool.get('ir.actions.act_window')
        wizard = self.browse(cr, uid, ids[0], context)
        sale_ids = context.get('active_ids', [])
        print wizard.advance_payment_method
        if wizard.advance_payment_method == 'all':
            # create the final invoices of the active sales orders
            res = sale_obj.manual_invoice(cr, uid, sale_ids, context)
#             print res
#             raise osv.except_osv(
#                 _('Error!'),
#                 _('applico'))
            if context.get('open_invoices', False):
                return res
            return {'type': 'ir.actions.act_window_close'}


        if wizard.advance_payment_method == 'lines':
            # open the list view of sales order lines to invoice
            res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context)
            res['context'] = {
                'search_default_uninvoiced': 1,
                'search_default_order_id': sale_ids and sale_ids[0] or False,
            }
            return res
        assert wizard.advance_payment_method in ('fixed', 'percentage')

        inv_ids = []
        for sale_id, inv_values in self._prepare_advance_invoice_vals(cr, uid, ids, context=context):
            inv_ids.append(self._create_invoices(cr, uid, inv_values, sale_id, context=context))
        if context.get('open_invoices', False):
            return self.open_invoices( cr, uid, ids, inv_ids, context=context)
        return {'type': 'ir.actions.act_window_close'}


class sale_order(osv.osv):

    _inherit = "sale.order"

    def _prepare_order_line_procurement(self, cr, uid, order, line, group_id=False, context=None):
        vals = super(sale_order, self)._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context)

        vals['product_qty'] = line.product_uom_qty - line.qty_delivery
        vals['product_uos_qty'] = line.product_uom_qty - line.qty_delivery
#         print vals
#         raise osv.except_osv(_('Error !'), _('This button still in progress mode'))
        return vals


    def _count_all(self, cr, uid, ids, field_name, arg, context=None):
        return {
            sale.id: {
                'do_count': len(sale.picking_ids),
                'invoice_count': len(sale.invoice_ids),
            }
            for sale in self.browse(cr, uid, ids, context=context)
        }
    _columns = {
        'do_count': fields.function(_count_all, type='integer', string='Delivery Orders', multi=True),
        'invoice_count': fields.function(_count_all, type='integer', string='Invoices', multi=True),
        'order_policy': fields.selection([
                ('manual', 'On Demand'),
                ('picking', 'On Delivery Order'),
            ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
            help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""),
        'other_policy': fields.selection([
                ('none', 'None'),
                ('picking_only', 'DO Confirm and Manual Invoicing'),
                ('invoice_picking', 'Auto Confirm DO and Invoices'),
            ], 'Other Option', required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'invoice_date': fields.date('Invoice Date'),
        'do_date': fields.datetime('DO Done Date', help="Date Done For DO."),

    }
    _defaults = {
             'order_policy': 'picking',
             'other_policy': 'none',
         }

    def action_cancel_draft(self, cr, uid, ids, context=None):
        if not len(ids):
            return False
        sale_order_line_obj = self.pool.get('sale.order.line')
        self.write(cr, uid, ids, {'state':'draft','shipped':0})

        for p_id in ids:
            # Deleting the existing instance of workflow for PO
            workflow.trg_delete(uid, 'sale.order', p_id, cr)
            workflow.trg_create(uid, 'sale.order', p_id, cr)
        for sale in self.browse(cr, uid, ids, context=context):
            for lines in sale.order_line:
                sale_order_line_obj.write(cr, uid, [lines.id], {'state':'draft'})
        return True
 
    def action_cancel(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        sale_order_line_obj = self.pool.get('sale.order.line')
        proc_obj = self.pool.get('procurement.order')
        stock_obj = self.pool.get('stock.picking')
        for sale in self.browse(cr, uid, ids, context=context):
            for line in sale.order_line:
                if line.procurement_ids:
                    proc_ids = []
                    proc_ids += [x.id for x in line.procurement_ids]
                    proc_obj.unlink(cr, uid, proc_ids, context=context)
                    #first check them to see if they are in exception or not (one of the related moves is cancelled)


            for pick in sale.picking_ids:
                if pick.state not in ('draft', 'cancel'):
                    raise osv.except_osv(
                        _('Cannot cancel sales order!'),
                        _('You must first cancel all delivery order(s) attached to this sales order.'))
            stock_obj.signal_workflow(cr, uid, [p.id for p in sale.picking_ids], 'button_cancel')
        return super(sale_order, self).action_cancel(cr, uid, ids, context=context)

    def action_invoice_create(self, cr, uid, ids, grouped=False, states=None, date_invoice = False, context=None):
        if states is None:
            states = ['confirmed', 'done', 'exception']
        res = False
        invoices = {}
        invoice_ids = []
        invoice = self.pool.get('account.invoice')
        obj_sale_order_line = self.pool.get('sale.order.line')

        partner_currency = {}
        # If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
        # last day of the last month as invoice date
#         raise osv.except_osv(
#             _('Error!'),
#             _('applico'))
        if date_invoice:
            context = dict(context or {}, date_invoice=date_invoice)


        for o in self.browse(cr, uid, ids, context=context):
            currency_id = o.pricelist_id.currency_id.id
            if o.invoice_date:
                context['date_invoice'] = o.invoice_date
            if (o.partner_id.id in partner_currency) and (partner_currency[o.partner_id.id] <> currency_id):
                raise osv.except_osv(
                    _('Error!'),
                    _('You cannot group sales having different currencies for the same partner.'))

            partner_currency[o.partner_id.id] = currency_id
            lines = []
            for line in o.order_line:
                if line.invoiced:
                    continue
                elif (line.state in states):
                    lines.append(line.id)
            created_lines = obj_sale_order_line.invoice_line_create(cr, uid, lines)
            if created_lines:
                invoices.setdefault(o.partner_invoice_id.id or o.partner_id.id, []).append((o, created_lines))
        if not invoices:
            for o in self.browse(cr, uid, ids, context=context):
                for i in o.invoice_ids:
                    if i.state == 'draft':
                        return i.id
        for val in invoices.values():
            if grouped:
                res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x, y: x + y, [l for o, l in val], []), context=context)
                invoice_ref = ''
                origin_ref = ''
                for o, l in val:
                    invoice_ref += (o.client_order_ref or o.name) + '|'
                    origin_ref += (o.origin or o.name) + '|'
                    self.write(cr, uid, [o.id], {'state': 'progress'})
                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
                    self.invalidate_cache(cr, uid, ['invoice_ids'], [o.id], context=context)
                #remove last '|' in invoice_ref
                if len(invoice_ref) >= 1:
                    invoice_ref = invoice_ref[:-1]
                if len(origin_ref) >= 1:
                    origin_ref = origin_ref[:-1]
                invoice.write(cr, uid, [res], {'origin': origin_ref, 'name': invoice_ref})
            else:
                for order, il in val:
                    res = self._make_invoice(cr, uid, order, il, context=context)
                    invoice_ids.append(res)
                    self.write(cr, uid, [order.id], {'state': 'progress'})
                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
                    self.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
                    if order.other_policy == 'invoice_picking':

                        workflow.trg_validate(uid, 'account.invoice', res, 'invoice_open', cr)
        move_obj = self.pool.get("stock.move")
        for order in self.browse(cr, uid, ids, context=context):
            if order.order_policy == 'picking':
                for picking in order.picking_ids:
                    move_obj.write(cr, uid, [x.id for x in picking.move_lines], {'invoice_state': 'invoiced'}, context=context)

        return res

    def revert_invoice(self, cr, uid, ids, context=None):
        wkf_instance_obj = self.pool.get('workflow.instance')
        wkf_workitem_obj = self.pool.get('workflow.workitem')
        for sale in self.browse(cr, uid, ids, context=context):
            for inv in sale.invoice_ids:
                if inv.state not in ('cancel'):
                    
                    raise osv.except_osv(
                        _('Cannot cancel this sales order!'),
                        _('First cancel all invoices attached to this sales order.'))

            self.write(cr, uid, sale.id, {'state': 'manual'})
            found_data = False
            wkf_instance_ids = wkf_instance_obj.search(cr, uid, [('res_type', '=', 'sale.order'),('state', '=', 'active'),('res_id', '=', sale.id)], context=None)
            if wkf_instance_ids:
                wkf_instance_id = wkf_instance_ids[0]
                wkf_workitem_ids = wkf_workitem_obj.search(cr, uid, [('inst_id', '=', wkf_instance_id),('act_id', '=', 17)], context=None)
                if wkf_workitem_ids:
                    found_data = True
                    workflow.trg_validate(uid, 'sale.order', sale.id, 'revert_invoice', cr)
                else:
                    wkf_workitem_obj.create(cr, uid, {'act_id': 13, 'inst_id': wkf_instance_id,'state':'complete'})
#         raise osv.except_osv(_('Invalid Action!'), _('Why.'))
        return True

    def unlink(self, cr, uid, ids, context=None):
        raise osv.except_osv(_('Invalid Action!'), _('cannot delete Sales Order except of cancel it.'))
        return super(sale_order, self).unlink(cr, uid, unlink_ids, context=context)

    def action_ship_create(self, cr, uid, ids, context=None):
        """Create the required procurements to supply sales order lines, also connecting
        the procurements to appropriate stock moves in order to bring the goods to the
        sales order's requested location.

        :return: True
        """
        
        procurement_obj = self.pool.get('procurement.order')
        sale_line_obj = self.pool.get('sale.order.line')
        picking_obj = self.pool.get('stock.picking')
        spo_obj = self.pool.get('stock.pack.operation')
#         raise osv.except_osv(_('Invalid Action!'), _('Why.'))
        for order in self.browse(cr, uid, ids, context=context):
            proc_ids = []
            vals = self._prepare_procurement_group(cr, uid, order, context=context)
            if not order.procurement_group_id:
                group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context)
                order.write({'procurement_group_id': group_id})

            for line in order.order_line:
                #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate)
                if line.procurement_ids:
                    #first check them to see if they are in exception or not (one of the related moves is cancelled)
                    procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']])
                    line.refresh()
                    #run again procurement that are in exception in order to trigger another move
                    for x1 in line.procurement_ids:
                        print x1.state 
                    proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')]
                    procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context)
                elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context):
                    if (line.state == 'done') or not line.product_id:
                        continue
                    vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context)
                    proc_id = procurement_obj.create(cr, uid, vals, context=context)
                    proc_ids.append(proc_id)
            #Confirm procurement order such that rules will be applied on it
            #note that the workflow normally ensure proc_ids isn't an empty list
            procurement_obj.run(cr, uid, proc_ids, context=context)

            #if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO
            if order.state == 'shipping_except':
                val = {'state': 'progress', 'shipped': False}

                if (order.order_policy == 'manual'):
                    for line in order.order_line:
                        if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
                            val['state'] = 'manual'
                            break
                order.write(val)
            if order.other_policy in ('picking_only', 'invoice_picking'):

                for picking in order.picking_ids:
                    processed_ids = []
                    packops2 = spo_obj.search(cr, uid, [('picking_id', '=', picking.id)])
                    for packop2 in packops2:
                        spo_obj.unlink(cr, uid, [packop2], context=context)

                    if not picking.pack_operation_ids:
                        picking.do_prepare_partial()
                    for op in picking.pack_operation_ids:
                        pack_datas = {
                            'product_id': op.product_id.id,
                            'product_uom_id': op.product_uom_id.id,
                            'product_qty': op.product_qty,
                            'package_id': op.package_id.id,
                            'lot_id': op.lot_id.id,
                            'location_id': op.location_id.id,
                            'location_dest_id': op.location_dest_id.id,
                            'result_package_id': op.result_package_id.id,
                            'date': picking.date_done or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                            'owner_id': op.owner_id.id,
                        }
                        if op.id:
                            op.write(pack_datas)
                            processed_ids.append(op.id)
    
                    # Delete the others
                    packops = spo_obj.search(cr, uid, ['&', ('picking_id', '=', picking.id), '!', ('id', 'in', processed_ids)])
                    for packop in packops:
                        packop.unlink()
    
    
#                     picking_obj.do_partial(cr, uid, [picking_id], partial_data, context=context)
                    picking_obj.do_transfer(cr, uid, [picking.id], context=context)

#                 raise osv.except_osv(_('Invalid Action!'), _('Why.'))
                if order.other_policy == 'invoice_picking' and order.order_policy == 'manual':
    
    #                     raise osv.except_osv(_('Error!'), _('You cannot remove the picking which is in %s state!')%('',))
                    workflow.trg_validate(uid, 'sale.order', order.id, 'manual_invoice', cr)
                    
#         raise osv.except_osv(_('Invalid Action!'), _('Xxxxx'))
        return True

    def recreate_picking(self, cr, uid, ids, context=None):
        for sale in self.browse(cr, uid, ids, context=context):
            data_process = False
#             raise osv.except_osv(_('Error !'), _('This button still in progress mode'))
            for lines in sale.order_line:
                if (lines.product_uom_qty - lines.qty_delivery) > 0:
                    data_process = True
                    continue
            if data_process == False:
                return True
            picking_ids = self._picking_recreate(cr, uid, [sale.id], context=context)
        return True

    def _picking_recreate(self, cr, uid, ids, context=None):
        """Create the required procurements to supply sales order lines, also connecting
        the procurements to appropriate stock moves in order to bring the goods to the
        sales order's requested location.

        :return: True
        """
        procurement_obj = self.pool.get('procurement.order')
        sale_line_obj = self.pool.get('sale.order.line')
        picking_obj = self.pool.get('stock.picking')
        spo_obj = self.pool.get('stock.pack.operation')
        for order in self.browse(cr, uid, ids, context=context):
            proc_ids = []
            vals = self._prepare_procurement_group(cr, uid, order, context=context)
            
            if not order.procurement_group_id:
                group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context)
                order.write({'procurement_group_id': group_id})

            for line in order.order_line:
                #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate)

                if not (line.product_uom_qty - line.qty_delivery) > 0:
                    continue
#                 if line.procurement_ids:
#                     print '1'
#                     #first check them to see if they are in exception or not (one of the related moves is cancelled)
#                     procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']])
#                     line.refresh()
#                     #run again procurement that are in exception in order to trigger another move
#                     proc_ids += [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')]
#                     procurement_obj.reset_to_confirmed(cr, uid, proc_ids, context=context)
#                 
                if sale_line_obj.need_procurement(cr, uid, [line.id], context=context):
                    if (line.state == 'done') or not line.product_id:
                        continue
                    vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context)
                    proc_id = procurement_obj.create(cr, uid, vals, context=context)
                    proc_ids.append(proc_id)

            #Confirm procurement order such that rules will be applied on it
            #note that the workflow normally ensure proc_ids isn't an empty list
            procurement_obj.run(cr, uid, proc_ids, context=context)

            #if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO
            if order.state == 'shipping_except':
                val = {'state': 'progress', 'shipped': False}

                if (order.order_policy == 'manual'):
                    for line in order.order_line:
                        if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
                            val['state'] = 'manual'
                            break
                order.write(val)

            if order.other_policy in ('picking_only', 'invoice_picking'):

                for picking in order.picking_ids:
                    if picking.state in ('cancel','done'):
                        continue
                    processed_ids = []
                    if not picking.pack_operation_ids:
                        picking.do_prepare_partial()
                    for op in picking.pack_operation_ids:
                        pack_datas = {
                            'product_id': op.product_id.id,
                            'product_uom_id': op.product_uom_id.id,
                            'product_qty': op.product_qty,
                            'package_id': op.package_id.id,
                            'lot_id': op.lot_id.id,
                            'location_id': op.location_id.id,
                            'location_dest_id': op.location_dest_id.id,
                            'result_package_id': op.result_package_id.id,
                            'date': picking.date_done or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                            'owner_id': op.owner_id.id,
                        }
                        if op.id:
                            op.write(pack_datas)
                            processed_ids.append(op.id)
    
                    # Delete the others
                    packops = spo_obj.search(cr, uid, ['&', ('picking_id', '=', picking.id), '!', ('id', 'in', processed_ids)])
                    for packop in packops:
                        packop.unlink()

                    picking_obj.do_transfer(cr, uid, [picking.id], context=context)

        return True

sale_order()

class sale_advance_payment_inv(osv.osv_memory):

    _inherit = "sale.advance.payment.inv"

    _columns = {
        'advance_payment_method':fields.selection(
            [('all', 'Invoice the whole sales order')],
            'What do you want to invoice?', required=True,
            help="""Use All to create the final invoice."""),
    }

sale_advance_payment_inv()

class sale_order_line(osv.osv):

    _inherit = "sale.order.line"

    def default_get(self, cr, uid, fields, context=None):
#        raise osv.except_osv(_('Error !'), _('This button still in progress mode'))
        if context is None:
            context = {}
        sale_order_obj = self.pool.get('sale.order')
        res = super(sale_order_line, self).default_get(cr, uid, fields, context=context)
        model = context and 'active_model' in context and context.get(('active_model')) or False
        if model == 'sale.order':
            for lines in sale_order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context):
                if not lines.partner_id:
                    raise osv.except_osv(_('Error !'), _('please select the customer before adding lines'))
                if 'company_id' in fields:
                    res.update({'company_id': lines.company_id.id})
                if 'partner_id_parent' in fields:
                    res.update({'partner_id_parent': lines.partner_id.id})
                if 'pricelist_id_parent' in fields:
                    res.update({'pricelist_id_parent': lines.pricelist_id.id})
                if 'date_order_parent' in fields:
                    res.update({'date_order_parent': lines.date_order})
                if 'fiscal_position_id_parent' in fields:
                    res.update({'fiscal_position_id_parent': lines.fiscal_position.id})
                if 'order_id' in fields:
                    res.update({'order_id': lines.id})
        return res


    def confirm_add_line(self, cr, uid, ids, context=None):
        self.button_confirm(cr, uid, ids, context)
#         mod_obj = self.pool.get('ir.model.data')
#         dummy, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'purchase', 'purchase_form_action'))
#         action = self.pool.get('ir.actions.act_window').read(cr, uid, action_id, context=context)
#         res = mod_obj.get_object_reference(cr, uid, 'purchase', 'purchase_order_form')
#         action['views'] = [(res and res[1] or False, 'form')]
#         action['res_id'] = po_id or False
        return {'type': 'ir.actions.act_window_close'}

    def _qty_delivery(self, cr, uid, ids, name, arg, context=None):
        if not ids: return {}
        res = {}
        stock_move_obj = self.pool.get('stock.move')
        for obj in self.browse(cr, uid, ids, context=context):
            qty_delivery = 0.00
            proc_ids = []
            for proc in obj.procurement_ids:
                proc_ids.append(proc.id)
            move_ids = stock_move_obj.search(cr, uid, [('procurement_id','in',proc_ids),('state','!=','cancel')])
            if move_ids:
                for mv in stock_move_obj.browse(cr, uid, move_ids, context=context):
 
                    qty_delivery = qty_delivery +  mv.product_uom_qty
            res[obj.id] = qty_delivery
        return res


    _columns = {
        'partner_id_parent': fields.many2one('res.partner', 'Supplier'),
        'fiscal_position_id_parent': fields.many2one('account.fiscal.position', 'Fiscal Position'),
        'pricelist_id_parent': fields.many2one('product.pricelist', 'Pricelist'),
        'date_order_parent': fields.datetime('Date'),
        'qty_delivery': fields.function(_qty_delivery, type='float', string='Qty Receipt'),
    }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
